package com.sgcc.isc.util;



import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.sgcc.common.core.utils.DateUtils;
import com.sgcc.common.core.utils.StringUtils;
import com.sgcc.isc.exception.XmlStyleException;

/**
 * 解析xml
 * @author xpc
 * @version 2019-11-4
 */
public class XmlParser {

    /**
     *
     * @param clazz 目标实体类
     * @param xml   xml格式字符串
     * @param list  目标实体类结果集
     * @return 参数list为null时，返回clazz的实体类，不为null时，返回clazz实体类的结果集
     * @throws DocumentException            xml 格式异常
     * @throws IllegalAccessException       构造被private修饰
     * @throws InstantiationException       实体类无构造函数
     * @throws IntrospectionException       内省异常 属性类型不匹配
     * @throws InvocationTargetException    反射异常，实体类属性无get set方法，method.invoke抛出
     * @throws NoSuchFieldException         无目标属性异常
     * @throws ClassNotFoundException       无目标实体类异常
     * @throws XmlStyleException            自定义异常，xml格式不同
     */
    public static Object parseXml2Entity(Class clazz, String xml, List list)
            throws DocumentException, IllegalAccessException,
            InstantiationException, IntrospectionException,
            InvocationTargetException, NoSuchFieldException, ClassNotFoundException, XmlStyleException {
        Object target = null;
        // 字符串格式转换成Document
        Document doc = DocumentHelper.parseText(xml);
        // 获取根节点
        Element rootElement = doc.getRootElement();
        if (!"DBSET".equals(rootElement.getName())) {
            throw new XmlStyleException("与规定的xml格式不同！数据集节点不为<DBSET>。");
        }
        Field[] fields = clazz.getDeclaredFields();
        // 获取根节点下的所有节点
        for (Object o1 : rootElement.elements()) {
            target = clazz.newInstance();
            Element e = (Element) o1;
            if (!"R".equals(e.getName())) {
                // xml格式有问题，直接返回
                throw new XmlStyleException("与规定的xml格式不同！行节点不为<R>。");
            }
            // 节点为R，获取所有节点
            for (Object o2 : e.elements()) {
                Element e2 = (Element) o2;
                if (!"C".equals(e2.getName())) {
                    throw new XmlStyleException("与规定的xml格式不同！属性节点不为<C>。");
                }
                // 获取字段名
                String fieldName = e2.attributeValue("N");
                if (StringUtils.isBlank(fieldName)) {
                    throw new XmlStyleException("与规定的xml格式不同！字段值存储在<C>的非“N”的属性中。");
                }
                //xuxw 2020-11-4修改，解决了匹配传输进来的xml字符串中属性值是大写的适配问题

                for(int i=0; i < fields.length; i++){
                    if(fieldName.toUpperCase().equals(fields[i].getName().toUpperCase())){
                        Field field = clazz.getDeclaredField(fields[i].getName());
                        String typeName = field.getType().getSimpleName();
                        PropertyDescriptor pd = new PropertyDescriptor(fields[i].getName(), clazz);
                        // 调用set方法
                        Method setMethod = pd.getWriteMethod();
                        if (null != setMethod) {
                            if (!StringUtils.isEmpty(e2.getText()) || e2.hasContent()) {
                                if ("String".equals(typeName)) {
                                    setMethod.invoke(target, e2.getTextTrim());
                                } else if ("Date".equals(typeName)) {
                                    setMethod.invoke(target, DateUtils.parseDate(e2.getTextTrim()));
                                } else if ("Double".equals(typeName) || "double".equals(typeName)) {
                                    setMethod.invoke(target, Double.valueOf(e2.getTextTrim()));
                                } else if ("Integer".equals(typeName) || "int".equals(typeName)) {
                                    setMethod.invoke(target, Integer.valueOf(e2.getTextTrim()));
                                } else if ("Float".equals(typeName) || "float".equals(typeName)) {
                                    setMethod.invoke(target, Float.valueOf(e2.getTextTrim()));
                                } else if ("Long".equals(typeName) || "long".equals(typeName)) {
                                    setMethod.invoke(target, Long.valueOf(e2.getTextTrim()));
                                } else if ("Byte".equals(typeName) || "byte".equals(typeName)) {
                                    setMethod.invoke(target, Byte.valueOf(e2.getTextTrim()));
                                } else if ("Short".equals(typeName) || "short".equals(typeName)) {
                                    setMethod.invoke(target, Short.valueOf(e2.getTextTrim()));
                                } else if ("Boolean".equals(typeName) || "boolean".equals(typeName)) {
                                    setMethod.invoke(target, Boolean.valueOf(e2.getTextTrim()));
                                } else if ("List".equals(typeName)) {
                                    List innerList = new ArrayList();
                                    // 获取List泛型
                                    ParameterizedType pt = (ParameterizedType) field.getGenericType();
                                    Type type = pt.getActualTypeArguments()[0];
                                    // 是基础类型
                                    if (isBaseDataType(type)) {

                                    }
                                    // 不是基础类型
                                    Class innerClazz = Class.forName(type.getTypeName());
                                    setMethod.invoke(target, parseXml2Entity(innerClazz, e2.element("DBSET").asXML(), innerList));
                                } else {// 自定义实体类
                                    Class innerClazz = Class.forName(field.getType().getTypeName());
                                    setMethod.invoke(target, parseXml2Entity(innerClazz, e2.element("DBSET").asXML(), null));
                                }
                            }
                        }
                    }
                }

            }
            if (null != list) {
                list.add(target);
            }
        }
        if (null != list) {
            return list;
        } else {
            return target;
        }
    }

    private static boolean isBaseDataType(Type type) {
        String typeName = type.getTypeName();
        if (typeName.contains("java.lang") || typeName.contains("java.util.Date") ||
        typeName.contains("long") || typeName.contains("float") || typeName.contains("int") || typeName.contains("double")
        || typeName.contains("boolean") || typeName.contains("short") || typeName.contains("byte")) {
            return true;
        }
        return false;
    }

}
